#ifndef REDUNDANCYGENERATOR_H_
#define REDUNDANCYGENERATOR_H_

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <fstream>
#include <semaphore.h>

using namespace std;

#include "../exception/BadArgumentException.h"
#include "../exception/InitializationException.h"

using namespace ::miniprojeto::excpetion;

#include "../util/Lista_Enc.h"
#include "../util/Lista_Cresc.h"
#include "../util/XORByteArray.h"

using namespace ::miniprojeto::util;

#include "CRCBlocks.h"
#include "CRCWorker.h"
#include "CRCFinishListener.h"


namespace miniprojeto {
namespace Redundancy {
//! Gerência o processo de gerar as redundâncias para um arquivo.
/*! Esta classe é a classe principal do modulo Redundancy. Ela é responsável por conduzir e gerenciar
 * todo o processo de leitura, cálculo do resto CRC, geraçaõ do bloco redundante XOR e escrita destes
 * novos blocos no arquivo.
 * Esta classe utiliza de multithread para agilizar o processo do cálculo do CRC. Ela também implementa
 * a classe virtual pura CRCFinishListener. Ela é notificada pelas Threads quando suas execuções
 * terminam.
 */
class RedundancyGenerator : public CRCFinishListener {
protected:
	//! semáforo para esperar o termino dos workers.
	sem_t semWaitWorkers; 
	
	//! semáforo de exclusão mutua.
	sem_t mutex; 
	
	//! armazena o nro de CRCWorkes criadas que ainda não finalizaram sua execução.
	int workersCreated;
	
	//! lista encadeada em ordem crescente de blocos.
	/*!
		estrutura de dados para armazenar pacotes a serem gravados -> FilaPrioridade.
		É utilizada uma fila de prioridade para que seja controlado automaticamente a ordem
		em que os blocos serão gravados no arquivo através dos indices que são atribuidos
		a eles no momento em que são lidos do arquivo original.
	*/
	Lista_Cresc<CRCBlocks*>* blocksList;
	
	//! cadeia de caracteres que armazena o nome do original do Arquivo
	string originalFileName;
	
	//! cadeia de caracteres que armazena o nome do final do Arquivo
	string finalFileName;
	
	//! armazena o tamanho do arquivo original
	long fileLen;
	
	//! armazena o nro de blocos em que o arquivo original foi fragmentado
	int blocksNumber;
		
	//! fornece uma interface para ler dados de um Arquivo como se fossem cadeias de entrada.
	ifstream in;
	
	//! fornece uma interface para escrever dados para um Arquivos como se fossem cadeias de saída.
	ofstream out;
	
	//! Seta o número de CRCWorkers que serão criados. Método Threadsafe.
	/*!
	*	\sa waitingWorkers
	*	\sa mutex
	*	\param nWorkers novo valor a ser atribuído a watingWorkers
		utiliza-se de semáforos; é sincronizado
	*/
	void setWorkersCreated(int nWorkers); /*synchronized*/
	
	//! Este método apenas espera todas as threads criadas terminarem
	/*!
	*	\sa semWaitWorkers
	*/
	void waitWorkersFinished();
	
	//! Calcula o XOR binário de todos os blocos que estão em  blocksList e o armazena em  blocksList.
	/*!
		\sa blockList
	*/
	void evalueteXor();
	
	//! Escreve todos so blocos que estão em blockList no arquivo. Também remove-os de blockList.
	/*!
	*	\sa blocksList
		\sa out
	*/
	void writeBlocks();
	
	//! Escreve o cabeçalho do Arquivo de saída.
	/*!
		\sa out
	*/
	void writeFileHeader(); 
	
	//! Abre os arquivos de entrada e saida e obtém as caracteristicas principaos do arquivo de entrada.
	void openFiles();
	
	//! imprime o estado do cálculo
	/*!
		\param byteCount
	*/
	void printStatus(long byteCount);

public:
	
	//! Método sobreescrito da classe virtual pura CRCFinishListener.
	/*
	 	Contém a ação que deve ser tomada sempre que uma thread CRCWorker finalizar sua execução.
	 	\param worker CRCWorker que terminou cuja execução terminou.
	 	\sa CRCFinishListener
	 */
	virtual void recevFinishWorkNotification(void* worker);
	
	//! variável que representa o tamanho do bloc, no caso 4096 bytes
	static const int BLOCK_SIZE= 4096;
	
	//! construtor padrão
	/*!
		\sa semWaitWorkers
		\sa mutex
		\sa workersList
		
		incializa semáforos: semWaitWorkers; mutex;
		cria a lista: blocksList;
	*/
	RedundancyGenerator();
	
	//! destrutor
	/*!
		\sa semWaitWorkers
		\sa mutex
		\sa blocksList
		
		apaga a lista blocksList;
		destrói os semáforos: semWaitWorkers; mutex;
	*/
	~RedundancyGenerator();
	
	//! modifica o valor do nome original do Arquivo
	/*!
		\sa originalFileName
		\param fname novo valor a ser atribuído a originalFileName
	*/
	void setOriginalFileName(string fname);
	
	//! modifica o valor do nome final do Arquivo
	/*!
		\sa finalFileName
		\param fname novo valor a ser atribuído a finalFileName
	*/
	void setFinalFileName(string fname);
	
	//! Inicia o processo de geração da redundancia dos arquivos.
	/* Para este método ser evocado, é necessário setar os nomes dos arquivos de entrada
	 * e saida.
	 * \sa setOriginalFileName
	 * \sa setFinalFileName(
	 */
	void init();
};

}
}

#endif /*REDUNDANCYGENERATOR_H_*/